Passed
Pull Request — master (#164)
by Jan
03:39 queued 01:45
created

file-read.ts ➔ calculateMissingBytes   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
1
import * as fs from 'fs'
2
import { findId3TagPosition, getId3TagSize, Header } from './id3-tag'
3
import { fsReadPromise, getNextBufferSubarrayAsync, getNextBufferSubarraySync, processFile, processFileAsync } from './util-file'
4
5
const FileBufferSize = 20 * 1024 * 1024
6
7
type SuccessCallback = (err: null, buffer: Buffer|null) => void
8
type ErrorCallback = (err: Error, buffer: null) => void
9
type Callback = SuccessCallback & ErrorCallback
10
11
export function getId3TagDataFromFileSync(filepath: string): Buffer|null {
12
    return processFile(filepath, 'r', (fileDescriptor) => {
13
        const partialId3TagData = findPartialId3TagSync(fileDescriptor)
14
        return partialId3TagData ? completePartialId3TagData(
15
            fileDescriptor,
16
            partialId3TagData
17
        ) : null
18
    })
19
}
20
21
export function getId3TagDataFromFileAsync(filepath: string, callback: Callback) {
22
    processFileAsync(filepath, 'r', async (fileDescriptor) => {
23
        const partialId3TagData = await findPartialId3TagAsync(fileDescriptor)
24
        return partialId3TagData ? completePartialId3TagDataAsync(
25
            fileDescriptor,
26
            partialId3TagData
27
        ) : null
28
    }).then((data) => {
29
        callback(null, data)
30
    }).catch((error) => {
31
        callback(error, null)
32
    })
33
}
34
35
function findPartialId3TagSync(fileDescriptor: number): Buffer|null {
36
    const buffer = Buffer.alloc(FileBufferSize)
37
    let data
38
    while((data = getNextBufferSubarraySync(fileDescriptor, buffer, Header.size)).length > Header.size) {
39
        const id3TagPosition = findId3TagPosition(data)
40
        if(id3TagPosition !== -1) {
41
            return data.subarray(id3TagPosition)
42
        }
43
        buffer.copyWithin(0, buffer.length - Header.size)
44
    }
45
    return null
46
}
47
48
async function findPartialId3TagAsync(fileDescriptor: number): Promise<Buffer|null> {
49
    const buffer = Buffer.alloc(FileBufferSize)
50
    let data
51
    while((data = await getNextBufferSubarrayAsync(fileDescriptor, buffer, Header.size)).length > Header.size) {
52
        const id3TagPosition = findId3TagPosition(data)
53
        if(id3TagPosition !== -1) {
54
            return data.subarray(id3TagPosition)
55
        }
56
        buffer.copyWithin(0, buffer.length - Header.size)
57
    }
58
    return null
59
}
60
61
function calculateMissingBytes(id3TagSize: number, id3TagBuffer: Buffer): number {
62
    return Math.max(0, id3TagSize - id3TagBuffer.length)
63
}
64
65
function completePartialId3TagData(fileDescriptor: number, partialId3TagData: Buffer): Buffer {
66
    const id3TagSize = getId3TagSize(partialId3TagData)
67
    const missingBytesCount = calculateMissingBytes(id3TagSize, partialId3TagData)
68
    if(missingBytesCount) {
69
        const id3TagRemainingBuffer = Buffer.alloc(missingBytesCount, 0x00)
70
        fs.readSync(fileDescriptor, id3TagRemainingBuffer)
71
        return Buffer.concat([
72
            partialId3TagData,
73
            id3TagRemainingBuffer
74
        ])
75
    }
76
    return partialId3TagData.subarray(0, id3TagSize)
77
}
78
79
async function completePartialId3TagDataAsync(fileDescriptor: number, partialId3TagData: Buffer): Promise<Buffer> {
80
    const id3TagSize = getId3TagSize(partialId3TagData)
81
    const missingBytesCount = calculateMissingBytes(id3TagSize, partialId3TagData)
82
    if(missingBytesCount) {
83
        const id3TagRemainingBuffer = Buffer.alloc(missingBytesCount, 0x00)
84
        await fsReadPromise(fileDescriptor, {buffer: id3TagRemainingBuffer})
85
        return Buffer.concat([
86
            partialId3TagData,
87
            id3TagRemainingBuffer
88
        ])
89
    }
90
    return partialId3TagData.subarray(0, id3TagSize)
91
}